﻿using System;
using System.Linq;
using System.Web;
using System.Collections.Specialized;
using System.Collections;
using Medianamik.Core;

namespace Medianamik.UI
{
    public class MedianamikSiteMapProvider : StaticSiteMapProvider
    {
        private static object _lock = new object();
        private SiteMapNode _siteMapNode;

        public MedianamikSiteMapProvider()
        {
            EnableLocalization = true;
        }

        public override SiteMapNode BuildSiteMap()
        {
            if (_siteMapNode != null)
            {
                return _siteMapNode;
            }
            lock (_lock)
            {
                if (_siteMapNode != null)
                {
                    return _siteMapNode;
                }
                try
                {
                    var rootNode = new SiteMapNode(this, "root", "/Medianamik");
                    AddNode(rootNode);

                    var allModules = ConfigManager.GetAll<MedianamikModule>().ToArray();
                    var allPages = ConfigManager.GetAll<MedianamikPage>()
                                    .OrderBy(p=>p.Position).ToArray();
                    var allMenuGroups = ConfigManager.GetAll<MenuGroup>()
                                    .OrderBy(p=>p.Position).ToArray();

                    //TODO: Erreur - C'est pas parce qu'on a pas de MenuGroup qu'on est un rootpage...
                    //les rootpages ne tiennent pas compte du Module...
                    var rootPages = allPages.Where(p => !p.MenuGroupId.HasValue);

                    foreach (var page in rootPages)
                    {
                        AddPage(page.ID
                            , page.FileName
                            , string.Empty
                            , default(Guid?)
                            , page.DisplayInMenu
                            , page.GetRoles().Select(r => r.RoleName ).ToList()
                            , rootNode);
                    }

                    foreach (var menuGroup in allMenuGroups)
                    {
                        var pages = (from page in allPages
                                     where page.MenuGroupId.HasValue
                                     && page.MenuGroupId.Value.Equals(menuGroup.ID)
                                     orderby page.Position
                                     select page).ToArray();

                        if (pages.Length > 0)
                        {
                            var menuGroupNode = AddMenuGroup(menuGroup.ID, menuGroup.CssClass, menuGroup.Name, rootNode,
                                menuGroup.ImageUrl, menuGroup.SelectedImageUrl, menuGroup.MasterPanelImageUrl);

                            var firstMenuItem = pages.FirstOrDefault(p => p.DisplayInMenu);

                            foreach (var page in pages)
                            {
                                var moduleAlias = string.Empty;
                                Guid? searchPageId = null;
                                if (page.ModuleId.HasValue)
                                {
                                    moduleAlias = (from module in allModules
                                                   where module.ID.Equals(page.ModuleId.Value)
                                                   select module.Alias).FirstOrDefault() ?? string.Empty;

                                    searchPageId = (from module in allModules
                                                    where module.ID.Equals(page.ModuleId.Value)
                                                    select module.SearchResultPageId).FirstOrDefault();
                                }

                                var pageNode = AddPage(page.ID
                                    , page.FileName
                                    , moduleAlias
                                    , searchPageId
                                    , page.DisplayInMenu
                                    , page.GetRoles().Select(r => r.RoleName).ToList()
                                    , menuGroupNode);

                                if (page == firstMenuItem)
                                {
                                    //Le premier item de menu ajoute un lien sur le MenuGroup
                                    menuGroupNode.Url = pageNode.Url; 
                                }
                            }
                        }
                    }

                    _siteMapNode = rootNode;
                }
                catch(Exception ex)
                {
                    Clear(); //
                    throw;
                }
            }

            return _siteMapNode;
        }

        /// <summary>
        /// Reconstruire le SiteMap si un changement a été fait à la structure;
        /// </summary>
        public void Rebuild()
        {
            lock (_lock)
            {
                Clear();
                _siteMapNode = null;

            }
        }

        protected override SiteMapNode GetRootNodeCore()
        {
            BuildSiteMap();
            return _siteMapNode;
        }

        public override bool IsAccessibleToUser(HttpContext context, SiteMapNode node)
        {
            if (node == null)
            {
                throw new ArgumentNullException("node");
            }

            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            if (!SecurityTrimmingEnabled)
            {
                return true;
            }

            if (node.Roles == null || node.Roles.Count == 0)
            {
                return true; // Si aucun Role n'est assigné à la page, elle est accessible
            }

            if (context.User == null)
                return false;

            //L'utilisateur doit avoir tous les rôles nécessaires
            //if (node.Roles != null)
            //{
            //    foreach (string role in node.Roles)
            //    {
            //        if (context.User != null && context.User.IsInRole(role))
            //        {
            //            return true;
            //        }
            //    }
            //}

            return node.Roles.Cast<string>().All(r => context.User.IsInRole(r));
        }

        private SiteMapNode AddPage(Guid pPageID
            , string pFileName
            , string pModuleAlias
            , Guid? pSearchPageId
            , bool pDisplayInMenu
            , IList pRoles
            , SiteMapNode pParentNode)
        {
            if (pFileName == null)
            {
                throw new ArgumentNullException("pFileName");
            }
            if (pParentNode == null)
            {
                throw new ArgumentNullException("pParentNode");
            }
            if (pRoles == null)
            {
                pRoles = new ArrayList();
            }

            var pageAttributes = new NameValueCollection();
            pageAttributes.Add("DisplayInMenu", pDisplayInMenu.ToString());
            pageAttributes.Add("moduleAlias", pModuleAlias);
            pageAttributes.Add("searchPageId", pSearchPageId.HasValue ? pSearchPageId.Value.ToString() : string.Empty);

            var pageExplicitResourceKeys = new NameValueCollection();
            pageExplicitResourceKeys.Add("title", "SiteMap");
            pageExplicitResourceKeys.Add("title", pFileName);

            pageExplicitResourceKeys.Add("moduleTitle", "SiteMap");
            pageExplicitResourceKeys.Add("moduleTitle", string.IsNullOrEmpty(pModuleAlias) ? pFileName : pModuleAlias);

            var pageNode = new SiteMapNode(this
                , pPageID.ToString()
                , FormatUrl(pModuleAlias, pFileName)
                , string.Concat("[", pFileName, "]")  /*Title => voir explicitResourceKeys*/
                , string.Empty
                , pRoles
                , pageAttributes
                , pageExplicitResourceKeys
                , string.Empty);

            AddNode(pageNode, pParentNode);

            return pageNode;
        }

        private SiteMapNode AddMenuGroup(Guid pId,string pClassName, string pName, SiteMapNode pParentNode,
            string imageUrl, string selectedImageUrl, string masterPanelImageUrl)
        {

            if (pParentNode == null)
            {
                throw new ArgumentNullException("pParentNode");
            }

            var menuGroupAttributes = new NameValueCollection();
            menuGroupAttributes.Add("CssClass", pClassName);
            menuGroupAttributes.Add("SelectedImageUrl", selectedImageUrl);
            menuGroupAttributes.Add("ImageUrl", imageUrl);
            menuGroupAttributes.Add("MasterPanelImageUrl", masterPanelImageUrl);

            var menuGroupExplicitResourceKeys = new NameValueCollection();
            menuGroupExplicitResourceKeys.Add("title", "SiteMap");
            menuGroupExplicitResourceKeys.Add("title", "menuGroup_Title_" + pId.ToString("N"));
            menuGroupExplicitResourceKeys.Add("description", "SiteMap");
            menuGroupExplicitResourceKeys.Add("description", "menuGroup_Description_" + pId.ToString("N"));

            var menuGroupNode = new SiteMapNode(this
                , pId.ToString()
                , null /*Url*/
                , string.Empty
                , null /*Description*/
                , Enumerable.Empty<string>().ToList()
                , menuGroupAttributes
                , menuGroupExplicitResourceKeys
                , string.Empty);

            AddNode(menuGroupNode, pParentNode);

            return menuGroupNode;
        }

        private string FormatUrl(string pModuleAlias, string pFilename)
        {
            //TODO:Ne pas Hardcoder Medianamik
            string url = string.Concat("/medianamik/"
                , string.IsNullOrEmpty(pModuleAlias) ? string.Empty : pModuleAlias + "/"
                , pFilename);

            return url;
        }
    }
}